“遗传算法”解决“背包问题”
遗传算法基本思想:
1) 一个种群有多个个体,每个个体有染色体和对应的基因
为了繁殖进行:
2) 选择:在残酷的世界中,适者生存,优胜略汰。
3) 重组:染色体交叉,基因重组
4) 突变:染色体上的基因小概率的突变 (一般给小数点后两位)
背包问题:
背包只能容得下一定重量b的物品,物品有m种,每种物品有自己的重量w(i)和价值v(i)(0<i<=m),从这些物品中选择装入背包,是背包不超过重量b,但价值又要最大。
运用动态规划,分支限界都可以达到效果,但不佳。
我用遗传算法解决:
一般人有多条染色体,但对于背包问题,一个解我们将看成一个个体,所以,一个个体只有一个染色体,一个染色体对应多个基因。如:100101010100111 表示装入背包的可能解。(具体情况具体分析)
遗传所做准备:
1) 用0表示“不选择装入”,1表示“装入”,形成一条基因链;100101010100111则表示“15种物品”装入或不装入背包的可能解。 ------- 此处用chrom[]存放基因,代表染色体
2) 一个基因对应一个个体。 ------- 此处用Population类或结构体声明其含有chrom[]等信息
3) 可能的解有很多,构成一个种群。 ------- 用Population类定义一个数组代表个体构成的种群newPop[]:存放新生代,oldPop[]:存放上一代
4) 适应度:适应度和目标函数是正相关的,所以需要物品价值和重量。
------- fitness,weight包含在Population类中
最大适应度:maxFitness,
最小适应度:minFitness,
总适应度:sumFitness,(帮助求突变和交叉的染色体)
平均适应度:avgFitness
遗传算法的函数:
基本:
1) InitPop() 初始化个体,使每个个体都有基因组
2) Statistics(*pop) 计算适应度(最大,最小,总的,平均的)
3) Selection(*pop) 通过选择种群中符合要求的父母去繁殖新代,返回这对父母的位置
4) crossover(*parent1,*parent2,pos) 传入要改的个体位置,随机产生交叉位置,用优良父母繁殖优良后代并替代传入个体位置
5) mutation(i) i为基因组基因的位置,逐个基因看是否要变异
6) generation() 对个体进行判断,若不符合要求,进行选择,重组,突变。
背包的适应性函数:(个体是一个解)
1) calWeight(pop) 计算个体的重量
2) calFit( pop ) 计算个体的价值
1 #include <AfxWin.h> 2 #include <stdlib.h> 3 #include <math.h> 4 #include <time.h> 5 #include <conio.h> 6 #include <stdio.h> 7 /** 8 * @author: ckj 9 * @date: 2012/12/27 10 */ 11 /*============= 一些必要的常量 ===============*/ 12 #define POP_SIZE 200 // 种群的规模 13 #define PRO_CROSS 0.618 // 交叉概率 14 #define PRO_MUTATE 0.03 // 变异概率 15 #define CHROM_SIZE 50 // 染色体长度 16 #define GENERATION_NUM 1000 // 繁殖代数 17 /*============================================*/ 18 19 // 个体类 20 struct population{ 21 unsigned int chrom[CHROM_SIZE]; // 基因组 22 double weight; // 背包重量 23 double fitness; // 适应度 24 unsigned int parent1,parent2,cross; // 双亲,交叉节点 25 }; 26 27 // 新生代和上一代的种群 28 struct population oldPop[POP_SIZE], newPop[POP_SIZE]; 29 30 // 物体的重量,收益,背包的容量 31 int weight[CHROM_SIZE], profit[CHROM_SIZE], contain; 32 33 // 种群的总的适应度,最小,最大,平均适应度。 34 double sumFitness, minFitness, maxFitness, avgFitness; 35 36 // 计算适应度时,用的惩罚函数系数 37 double alpha; 38 39 // 一个种群的最大和最小适应度个体 40 int minPop, maxPop; 41 42 /**============================================= 43 * 参数:chr为装入背包的一个可能解 44 * 用途:计算装入背包的一个可能解得(个体)重量 45 * 返回值:个体重量 46 ============================================**/ 47 double calWeight( unsigned int *chr ){ 48 double popSumWeight = 0; 49 for(int i = 0; i < CHROM_SIZE; i++){ 50 popSumWeight += (*chr) * weight[i]; 51 chr++; 52 } 53 return popSumWeight; 54 } 55 56 /**=============================== 57 * 参数:chr为装入背包的一个可能解 58 * 用途:计算装入背包的总价值 59 * 返回值:返回个体的价值 60 ================================*/ 61 double calFit( unsigned int *chr ){ 62 double popProfit = 0; 63 for(int i = 0; i < CHROM_SIZE; i++){ 64 popProfit += (*chr) * profit[i]; 65 chr++; 66 } 67 return popProfit; 68 } 69 70 /**======================================= 71 * 参数:传入一个种群 72 * 用途:计算种群的最大适应度和最小适应度 73 =======================================*/ 74 void statistics( struct population *pop ){ 75 double tmpFitness; 76 77 sumFitness = pop[0].fitness; 78 minFitness = pop[0].fitness; 79 minPop = 0; 80 maxFitness = pop[0].fitness; 81 maxPop = 0; 82 83 for( int i =1; i < POP_SIZE; i++ ){ 84 // 计算种群的总适应度 85 sumFitness += pop[i].fitness; 86 tmpFitness = pop[i].fitness; 87 // 选择最大适应度的个体的位置和适应度的值 88 if( (tmpFitness > maxFitness) && ((int)(tmpFitness*10)%10 == 0) ){ 89 maxFitness = pop[i].fitness; 90 maxPop = i; 91 } 92 // 选择最小适应度的个体的位置 93 if( (tmpFitness < minFitness) ){ 94 minFitness = pop[i].fitness; 95 minPop = i; 96 } 97 avgFitness = sumFitness / (float)POP_SIZE; 98 } 99 } 100 101 /**==================== 102 * 用途:报告种群信息 * 103 ====================**/ 104 void report( struct population *pop, int gen ){ 105 int popWeight = 0; 106 printf("\nThe generation is %d.\n", gen); // 输出种群的代数 107 // 输出种群中最大适应度个体的染色体信息 108 printf("The population chrom is: \n"); 109 for( int j = 0; j < CHROM_SIZE; j++ ){ 110 if( j % 5 == 0 ) {printf(" ");} 111 // 每个基因单位输出 112 printf("%1d", pop[maxPop].chrom[j]); 113 } 114 printf("\nThe population's max fitness is %d.", (int)pop[maxPop].fitness); 115 printf("\nThe population's max weight is %d.\n", (int)pop[maxPop].weight); 116 } 117 118 /**==================== 119 * 用途:初始化种群 * 120 ====================**/ 121 void initPop(){ 122 int i, j; 123 double tmpWeight; 124 bool isPop = false; 125 // 生成一个祖宗种群 126 for( i = 0; i < POP_SIZE; i++ ){ 127 while(!isPop){ 128 // 如果不满足条件,则继续随机产生直到有相同的个体 129 for( j = 0; j < CHROM_SIZE; j++ ){ 130 oldPop[i].chrom[j] = rand()%2; // 产生0/1的任意一个数 131 oldPop[i].parent1 = 0; 132 oldPop[i].parent2 = 0; 133 oldPop[i].cross = 0; 134 } 135 // 选择重量小于背包容量的个体,满足条件的个体 136 137 tmpWeight = calWeight( oldPop[i].chrom ); 138 if( tmpWeight <= contain ){ 139 oldPop[i].fitness = calFit( oldPop[i].chrom ); 140 oldPop[i].weight = tmpWeight; 141 oldPop[i].parent1 = 0; 142 oldPop[i].parent2 = 0; 143 oldPop[i].cross = 0; 144 isPop = true; 145 } 146 } 147 isPop = false; 148 } 149 } 150 /**========================================= 151 * 参数:用于带入概率参数:"交叉"/"变异" * 152 * 用途:概率选择试验 * 153 =========================================**/ 154 int excise( double probability ){ 155 double pp; 156 pp = (double)(rand()%20001/20000.0); 157 if( pp <= probability ) 158 return 1; 159 return 0; 160 } 161 162 /**==================== 163 * 用途:个体的选择 * 164 ====================**/ 165 int selection(int pop){ 166 double wheelPos, randNumber,partsum = 0; 167 int i = 0; 168 // 轮赌法 169 randNumber=(rand()%2001)/2000.0; 170 wheelPos = randNumber*sumFitness; 171 do { 172 partsum += oldPop[i].fitness; 173 i++; 174 }while( ( partsum < wheelPos) && (i < POP_SIZE) ); 175 return i-1; 176 } 177 178 /**======================================== 179 * 参数:父母染色体,即不被测试选到的染色体 180 * 用途:染色体交叉 181 * 返回值:1 182 *========================================*/ 183 int crossOver(unsigned int *parent1, unsigned int *parent2, int i){ 184 int j; // 基因组的基因位置 185 int crossPos; // 交叉点位置 186 if( excise(PRO_CROSS) ){ 187 crossPos = rand()%(CHROM_SIZE-1); 188 }else{ 189 crossPos = CHROM_SIZE - 1; 190 } 191 // 开始交叉 192 for( j = 0; j <= crossPos; j++ ){ 193 newPop[i].chrom[j] = parent1[j]; 194 } 195 for( j = crossPos+1; j < CHROM_SIZE; j++ ){ 196 newPop[i].chrom[j] = parent2[j]; 197 } 198 newPop[i].cross = crossPos; 199 return 1; 200 } 201 202 /**========================================== 203 * 参数:染色体基因组的某一位置突变 204 * 用途:染色体基因突变 205 * 返回值:若突变,则突变结果,否则,原样返回 206 *=========================================*/ 207 int mutation(unsigned int alleles){ 208 if( excise(PRO_MUTATE) ){ 209 // 满足突变概率,则此基因突变 210 alleles == 0? alleles = 1: alleles = 0; 211 } 212 return alleles; 213 } 214 215 /**==================================================== 216 * mate1,mate2是从种群中挑出的能来诞生新一代的个体位置 217 * 用途:种群群体更新 218 *=====================================================*/ 219 void generation(){ 220 unsigned int i, j, mate1, mate2; 221 double tmpWeight = 0; 222 bool notGen; 223 for( i = 0; i < POP_SIZE; i++){ 224 notGen = false; 225 // 需要繁殖 226 while( !notGen ){ 227 // 选择:选择有几率产生优良后代的父母个体位置 228 mate1 = selection(i); 229 mate2 = selection(i+1); 230 // 交叉:产生新一代替换掉不符合生存条件的i 231 crossOver( oldPop[mate1].chrom, oldPop[mate2].chrom, i); 232 for( j = 0; j < CHROM_SIZE; j++ ){ 233 // 变异:新个体一个一个基因位置代入,看是否要突变 234 newPop[i].chrom[j] = mutation(newPop[i].chrom[j]); 235 } 236 // 选择重量小于背包重量的个体,满足条件,则把价值给记录起来 237 tmpWeight = calWeight( newPop[i].chrom ); 238 if( tmpWeight <= contain ){ 239 newPop[i].fitness = calFit( newPop[i].chrom ); 240 newPop[i].weight = tmpWeight; 241 newPop[i].parent1 = mate1; 242 newPop[i].parent2 = mate2; 243 notGen = true; // 不需要再产生子代 244 } 245 } 246 } 247 } 248 249 void main(int argc, char* argv[]){ 250 int gen, oldMaxPop, k; 251 double oldMax; 252 // 输入每个物品的重量和价值,背包容量 253 printf("Please input the products\' weights:\n"); 254 for(int i = 0; i < CHROM_SIZE; i++ ) 255 scanf("%d", &weight[i]); 256 printf("\nPlease input the products\' porfits:\n"); 257 for(int j = 0; j < CHROM_SIZE; j++ ) 258 scanf("%d", &profit[j]); 259 contain = 1000; 260 gen = 0; // 代表第一代(主要用在输出显示上) 261 262 srand( (unsigned)time(NULL) ); // 置随机种子 263 initPop(); // 初始化种群 264 memcpy(&newPop,&oldPop,POP_SIZE*sizeof(struct population)); 265 statistics( newPop ); // 产生新种群的信息(适应度等) 266 report( newPop, gen ); 267 while( gen < GENERATION_NUM ){ 268 gen += 1; 269 if( gen % 100 == 0 ) 270 srand( (unsigned)time(NULL) ); // 每一百代产生新的随机数 271 oldMax = maxFitness; // 将最大适应度给上一代 272 oldMaxPop = maxPop; // 拥有最大适应度的个体位置给上一代 273 generation(); 274 statistics( newPop ); 275 276 // 如果新的最大适应度小于上一代最大适应度,则上一代活着 277 if( maxFitness < oldMax ){ 278 for( k = 0; k < CHROM_SIZE; k++ ){ 279 newPop[minPop].chrom[k] = oldPop[oldMaxPop].chrom[k]; 280 } 281 newPop[minPop].fitness = oldPop[oldMaxPop].fitness; 282 newPop[minPop].parent1 = oldPop[oldMaxPop].parent1; 283 newPop[minPop].parent2 = oldPop[oldMaxPop].parent2; 284 newPop[minPop].cross = newPop[minPop].cross; 285 statistics(newPop); 286 }else if( maxFitness > oldMax ){ 287 report(newPop, gen); 288 } 289 //保存新生代种群的信息到老一代种群信息空间 290 memcpy(&oldPop,&newPop,POP_SIZE*sizeof(struct population)); 291 } 292 }